/* 
 * File:   mpi_ub6.cpp
 * Author: sergey
 */

#include "DefineConfiguration.h"
#include <iostream>
#include <sstream>
#include <mpi/mpi.h>
#include <fstream>
#include <omp.h>
#include <vector>
#include "LibraryInterface.h"
#include <string>

#include "min7/genmin7v0.0.cpp"

#include <stdlib.h>
#include <signal.h>

int rank = 0, size = 1;

struct UInt128 {
    UInt64 l, r;
};

const UInt8 fComplication = 25;

UInt64 busy = 1,
	   doneFunctions = 0,
	   doneFunctionsP = 0;

MPI_Status state;

std::ofstream outputFile;

std::vector<UInt128> funcs;

UInt64 lnf, rnf;

/*
 * Функция, которая получает старое задание и отправляет новое.
 * Если stop == true, то рабочим узлам приказывает заканчивать работу, если нет,
 * то выдается новое задание, которое они и обрабатывают.
 */
inline void ub7_getNsend(bool stop) {
    UInt8 fc;
    doneFunctions++;
    MPI_Recv(&fc, 1, MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, 2, MPI_COMM_WORLD, &state);
    if ((doneFunctions & 0xFFFF) == 0)
        std::cout << "Processed functions " << std::dec << doneFunctions << '(' << doneFunctionsP << ')' << std::endl;
    if (fc >= fComplication) {
        /*std::cout << "Function (" << std::hex <<
                                funcs[state.MPI_SOURCE - 1].l << ',' <<
                                funcs[state.MPI_SOURCE - 1].r <<
                                ") has complication " << std::dec << (int) fc << std::endl;*/
        outputFile.write((char*)&funcs[state.MPI_SOURCE - 1], sizeof(UInt128));
        doneFunctionsP++;
    }
    if (stop) {
        funcs[state.MPI_SOURCE - 1].l = 0;
        funcs[state.MPI_SOURCE - 1].r = 0;
        MPI_Send(&funcs[state.MPI_SOURCE - 1], 2, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 3, MPI_COMM_WORLD);
    } else {
        funcs[state.MPI_SOURCE - 1].l = lnf;
        funcs[state.MPI_SOURCE - 1].r = rnf;
        MPI_Send(&funcs[state.MPI_SOURCE - 1], 2, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 1, MPI_COMM_WORLD);
    }
}


bool term_flag = false;

void term_handler(int i) {
    std::cout << "Terminating..." << std::endl;
    term_flag = true;
}

UInt64 get_iFs() {
    std::ifstream input("config.txt");
    if (!input.is_open()) return 0;
    UInt64 ifs;
    input >> ifs;
    input.close();
    return ifs;
}

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    std::ostringstream filename;

    if (rank == 0) {
        std::cout << "Starting task generator." << std::endl;        
        signal(SIGTERM, term_handler);        
        
        funcs.resize(size - 1);
        for(size_t i = size - 1; i--; ) {funcs[i].l = 0;funcs[i].r = 0;}  

        filename << LIBRARYDIRECTORIES << "/6_14/lpclasses14-15.txt";                
        std::ifstream inputFile(filename.str().c_str());
        filename.str("");
 
        std::vector<UInt64> lpclasses;       
        while(!inputFile.eof()) {
            UInt64 t;
            inputFile >> std::hex >> t;
            lpclasses.push_back(t);
        }
        inputFile.close();       
        
        filename << LIBRARYDIRECTORIES << "/7/ub7_25-26.bin";
        outputFile.open(filename.str().c_str(), std::ios::binary | std::ios_base::app);
        filename.str("");

        filename << LIBRARYDIRECTORIES << "/6_14/full14-15.bin";
        inputFile.open(filename.str().c_str(), std::ios::binary);
        filename.str("");
        
        UInt64 inputFileSize;
        inputFile.seekg(0, std::ios::end);
        inputFileSize = inputFile.tellg() / 8;
        
        UInt64 iFs_start = get_iFs();
        inputFile.seekg(iFs_start * 8, std::ios::beg);       
        
        std::cout << "Starting from function #" << iFs_start*63 << std::endl;

        for(UInt64 iFs = iFs_start; iFs < inputFileSize; iFs++) {
            if (inputFile.eof()) break;
            if (term_flag) {
                /* В данный момент при закрытии приложения просто
                 * записываются все считающиеся узлы в файл.
                 * TODO Надо бы все-таки сделать так. чтобы результаты
                 * обрабатывались. (учесть то, что возможно какие-то узлы "вырубились")
                 */
                for(; busy != 1; busy--) {
                    outputFile.write((char*)&funcs[busy - 1], sizeof(UInt128));
                    //ub7_getNsend(true);
                }
                outputFile.close();

                outputFile.open("config.txt");
                outputFile << iFs;
                outputFile.close();

                std::cout << "Processed functions " << std::dec << doneFunctions << '(' << doneFunctionsP << ')' << std::endl;
                std::cout << "Task generator ending work." << std::endl;

                MPI_Finalize();
                exit(EXIT_SUCCESS);
            }
            inputFile.read((char*)&lnf, sizeof(UInt64));
            for(size_t i = lpclasses.size(); i--; ) {
                rnf = lpclasses[i];
                if (busy == size) {
					ub7_getNsend(false);
                } else {
                    for(size_t j = 0; j < funcs.size(); j++)
                        if (funcs[j].l == 0 && funcs[j].r == 0) {
                            funcs[j].l = lnf;
                            funcs[j].r = rnf;
                            MPI_Send(&funcs[j], 2, MPI_UNSIGNED_LONG_LONG, j + 1, 1, MPI_COMM_WORLD);

                            busy++;
                            break;
                        }
                }
            }
        }
        inputFile.close();

        for(; busy != 1; busy--) ub7_getNsend(true);
        outputFile.close();
        std::cout << "Task generator ending work." << std::endl;
    } else {
        std::cout << "Starting processor: " << rank << std::endl;
        UInt128 f;        
        
        tf7_exp p;

        filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << Lib5::n << ".bin";
        nllib.load(filename.str());
        filename.str("");
        
        omp_set_dynamic(0);
		omp_set_num_threads(2);
        
        while(true) {
            
            MPI_Recv(&f, 2, MPI_UNSIGNED_LONG_LONG, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &state);

            if (state.MPI_TAG == 1) {
                UInt8 fc = genmin7(f.l >> 32, f.l & 0xFFFFFFFF, f.r >> 32, f.r & 0xFFFFFFFF, p);
                MPI_Send(&fc, 1, MPI_UNSIGNED_CHAR, 0, 2, MPI_COMM_WORLD);
            } else {
                std::cout << "Process " << std::dec << rank << " ending work." << std::endl;
                break;
            }            
        }              
    }

    MPI_Finalize();
    
    return (0);
}
